use crate::db::db_role;
use crate::db::entities::role::Model;
use crate::rest_api::api_role::{CreateParams, Role, SearchParams, UpdateParams};
use crate::rest_api::api_user::UserToken;
use crate::rest_api::defines::ADMIN;
use crate::rest_api::service::auth_check::check_is_admin;
use anyhow::Result;

use rbase::define::{ApiError, BaseError};
use rbase::id_utils::str2id;

pub async fn find_role_by_id(role_id: &String) -> Result<Role, ApiError> {
    let role_model = db_role::find_by_id(str2id(role_id)?)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(role_id.to_string()))?;
    Ok(Role::from_model(&role_model))
}

pub async fn delete_role_by_id(user_token: &UserToken, role_id: &String) -> Result<(), ApiError> {
    check_is_admin(user_token)?;
    if !user_token.is_admin {
        return Err(BaseError::PermissionDenied.into());
    }
    let role_model = db_role::find_by_id(str2id(role_id)?)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(role_id.to_string()))?;
    if role_model.role == Some(ADMIN.to_string()) {
        return Err(BaseError::PermissionDenied.into());
    }
    db_role::delete_by_id(role_model.id).await?;
    Ok(())
}

pub async fn update_role_by_id(user_token: &UserToken, role_id: &String, new_role: &UpdateParams) -> Result<(), ApiError> {
    check_is_admin(user_token)?;
    let old_model = db_role::find_by_id(str2id(role_id)?)
        .await?
        .ok_or_else(|| BaseError::ResourceNotFound(role_id.to_string()))?;
    if old_model.name == Some(ADMIN.to_string()) && new_role.role != ADMIN.to_string() {
        return Err(BaseError::PermissionDenied.into());
    }
    let new_model = Model {
        id: old_model.id,
        name: Some(new_role.name.clone()),
        ..Default::default()
    };
    db_role::update(new_model).await?;
    Ok(())
}

pub async fn create_role(user_token: &UserToken, new_role: &CreateParams) -> Result<Role, ApiError> {
    check_is_admin(user_token)?;
    let new_model = Model {
        name: Some(new_role.name.clone()),
        role: Some(new_role.role.clone()),
        ..Default::default()
    };
    let created_model = db_role::insert(new_model).await?;
    Ok(Role::from_model(&created_model))
}

pub async fn search(user_token: &UserToken, user_search_params: &SearchParams) -> Result<(Vec<Role>, i64), ApiError> {
    check_is_admin(user_token)?;
    let (roles, total) = db_role::search(&db_role::SearchParams {
        name: user_search_params.name.clone(),
        offset: user_search_params.offset,
        limit: user_search_params.limit,
        order_by: user_search_params.order_by.clone(),
        order: user_search_params.order.clone(),
    })
    .await?;
    Ok((roles.into_iter().map(|role| Role::from_model(&role)).collect(), total))
}
